/** \file DecoratedNodeType.h
* A decorator for a variable type in g-mini
*/

#ifndef _NODETYPE_DEECORATOR_H_
#define _NODETYPE_DEECORATOR_H_

#include "gminiGraph.h"
#include <string>
#include <assert.h>

using namespace std;

/**
* \class DecoratedNodeType
* \author Jad Makhlouta & Layale Constantine 
* \brief defines a decorated NodeType
*
* Since in ProgGraph, a variable type is represented by a data item, a member of ProgNode and since our g-mini graph 
* represents a recursive type (Array<Pair<int, int>>) by a recursive branch, we need to be able to decorate the data item in order
* to add extensions to the single node types int and bool such as Pair<int, int>
*
*/
class NodeTypeInterface
{
public:
	NodeType type;//<! A g-mini node type
	
	/*
	*\brief returns a string representation of a NodeType
	*/
	virtual string toString()=0;
	
	/*
	*\brief returns an additional node tag, specifically used for an expression NodeType to designate the operation used
	*/
	virtual string getAdditionalTag()=0;
	
	/*
	*\brief returns a pointer to the subtype of an array or the first subtype of a g-mini pair.
	*
	*/
	virtual NodeTypeInterface * getSubType1()=0;
	
	/*
	*\brief returns a pointer to the second subtype of a g-mini pair
	*/
	virtual NodeTypeInterface * getSubType2()=0;

	bool equals(NodeTypeInterface * dec)
	{
		if (dec==NULL && this==NULL)
			return true;
		if (dec==NULL || this==NULL) //one is NULL and other is not
			return false;
		if (type!=dec->type)
			return false;
		if (getAdditionalTag()!=dec->getAdditionalTag())
			return false;
		return (getSubType1()->equals(dec->getSubType1()) && getSubType2()->equals(dec->getSubType2()));
	}

	virtual ~NodeTypeInterface() {}
};

/**
* \class SimpleNodeType
* \brief a concrete DecoratedNodeType for simple types : Int and Bool
*
*/
class SimpleNodeType: public NodeTypeInterface //contains just the NodeType
{
public:
	SimpleNodeType(NodeType t)
	{
		type=t;
		assert(t!=ARRAY_TYPE && t!= PAIR_TYPE && t!=ARITH_OP && t!= BOOL_OP && t!= BOOL_COMP && t!=ERROR_NODE);
	}
	string toString(){ return  NodeType2string(type);}
	string getAdditionalTag() {return "";}
	NodeTypeInterface * getSubType1(){return NULL;}
	NodeTypeInterface * getSubType2(){return NULL;}
	~SimpleNodeType() {}
	
};

/**
* \class TaggedSimpleNodeType
* \brief a concrete DecoratedNodeType for tagged types: expressions (Arithmetic/Boolean operations and Comparisions) and errors 
* where a message is to be displayed
*
*/
class TaggedSimpleNodeType: public NodeTypeInterface
{
private:
	string tag; //<! the expression operator e.g. ("+","/","<"...)
public:
	TaggedSimpleNodeType(NodeType t, string tag)
	{
		type=t;
		this->tag=tag;
		assert(t==ARITH_OP || t== BOOL_OP || t== BOOL_COMP || t==ERROR_NODE);
	}
	string toString(){ return  NodeType2string(type) +" : "+ tag;}
	string getAdditionalTag() {return tag;}
	NodeTypeInterface * getSubType1(){return NULL;}
	NodeTypeInterface * getSubType2(){return NULL;}
	~TaggedSimpleNodeType() {}
};

/**
* \class ArrayNodeType
* \brief a concrete DecoratedNodeType for arrays only, it implements an actual decorator for an array's subtype.
*
*/
class DecoratedArrayNodeType: public NodeTypeInterface
{
private:
	NodeTypeInterface * subtype; //<! the array subtype to be decorated
public:
	DecoratedArrayNodeType(NodeTypeInterface * subtype)
	{
		type=ARRAY_TYPE;
		assert(subtype!=NULL);
		assert(subtype->type==ARRAY_TYPE || subtype->type==PAIR_TYPE || subtype->type==INT_TYPE || subtype->type==BOOL_TYPE);
		this->subtype=subtype;
	}
	string toString(){ return  NodeType2string(ARRAY_TYPE) +"< "+ subtype->toString() + " >";} 
	string getAdditionalTag() {return "";}
	NodeTypeInterface * getSubType1(){return subtype;}
	NodeTypeInterface * getSubType2(){return NULL;}
	~DecoratedArrayNodeType()
	{
		delete subtype;
	}
};
/**
* \class PairNodeType
* \brief a concrete DecoratedNodeType for Pairs only, here decorator idea is used (twice) for each pair's subtype
*
*/
class DecoratedPairNodeType: public NodeTypeInterface //
{
private:
	NodeTypeInterface * subtype1; //<! the first pair subtype to be decorated
	NodeTypeInterface * subtype2; //<! the second pair subtype to be decorated
public:
	DecoratedPairNodeType(NodeTypeInterface * subtype1, NodeTypeInterface * subtype2)
	{
		type=PAIR_TYPE;
		assert(subtype1!=NULL && subtype2!=NULL);
		assert(subtype1->type==ARRAY_TYPE || subtype1->type==PAIR_TYPE || subtype1->type==INT_TYPE || subtype1->type==BOOL_TYPE);
		assert(subtype2->type==ARRAY_TYPE || subtype2->type==PAIR_TYPE || subtype2->type==INT_TYPE || subtype2->type==BOOL_TYPE);
		this->subtype1=subtype1;
		this->subtype2=subtype2;
	}
	string toString(){ return  NodeType2string(PAIR_TYPE) +"< "+ subtype1->toString() + " , " + subtype2->toString() + " >";}
	string getAdditionalTag() {return "";}
	NodeTypeInterface * getSubType1(){return subtype1;}
	NodeTypeInterface * getSubType2(){return subtype2;}
	~DecoratedPairNodeType()
	{
		delete subtype1;
		delete subtype2;
	}
};

#endif
